package org.myspringframework.core;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.myspringframework.core.annotation.Component;
import org.myspringframework.core.annotation.Controller;
import org.myspringframework.core.annotation.Repository;
import org.myspringframework.core.annotation.Service;
import org.myspringframework.core.util.ClassUtil;
import org.myspringframework.core.util.CollectionUtils;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.stream.Collectors;

/**
 * @author jaymin<br>
 * 单例的Bean容器<br>
 * 2020/11/15 23:00
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanContainer {
    /**
     * 存放所有被配置标记的目标对象的Map
     */
    private final ConcurrentHashMap<Class<?>, Object> beanMap = new ConcurrentHashMap<>();
    /**
     * 加载Bean的注解列表
     */
    private static final List<Class<? extends Annotation>> BEAN_ANNOTATIONS
            = Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);

    /**
     * 获取Bean容器实例
     *
     * @return BeanContainer
     */
    public static BeanContainer getInstance() {
        return BeanContainerHolder.HOLDER.instance;
    }

    /**
     * 使用枚举实现单例模式
     */
    private enum BeanContainerHolder {
        /**
         * 装载容器实例
         */
        HOLDER;
        private BeanContainer instance;

        BeanContainerHolder() {
            instance = new BeanContainer();
        }
    }

    /**
     * 容器是否已经加载过Bean
     */
    private boolean loaded = false;

    public boolean isLoaded() {
        return loaded;
    }

    /**
     * Bean实例数量
     *
     * @return
     */
    public int size() {
        return beanMap.size();
    }

    /**
     * 扫描加载所有Bean
     *
     * @param packageName 包路径，扫描范围
     */
    public synchronized void loadBeans(String packageName) {
        // 判断容器是否被加载过
        if (isLoaded()) {
            log.warn("BeanContainer has been loaded.");
            return;
        }
        Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
        if (CollectionUtils.isEmpty(classSet)) {
            log.warn("extract nothing from packageName :{}", packageName);
            return;
        }
        // 找出标记了注解的类
        for (Class<?> clazz : classSet) {
            for (Class<? extends Annotation> beanAnnotation : BEAN_ANNOTATIONS) {
                // 如果类上面标记的注解存在与BEAN_ANNOTATIONS中
                if (clazz.isAnnotationPresent(beanAnnotation)) {
                    // key-Class value-Instance
                    beanMap.put(clazz, ClassUtil.newInstance(clazz, true));
                }
            }
        }
        loaded = true;
    }

    /**
     * 往容器新增一个Class对象和其对应的Bean实例
     *
     * @param clazz Class对象
     * @param bean  Bean实例
     * @return 原有的Bean实例，没有则返回null
     */
    public Object addBean(Class<?> clazz, Object bean) {
        return beanMap.put(clazz, bean);
    }

    /**
     * 移除容器中管理的一个对象
     *
     * @param clazz Class对象
     * @return 删除的Bean实例，没有则返回null
     */
    public Object removeBean(Class<?> clazz) {
        return beanMap.remove(clazz);
    }

    /**
     * 根据Class对象获取Bean实例
     *
     * @param clazz Class对象
     * @return
     */
    public Object getBean(Class<?> clazz) {
        return beanMap.get(clazz);
    }

    /**
     * 获取容器管理的所有Class对象集合
     *
     * @return Class集合
     */
    public Set<Class<?>> getClasses() {
        return beanMap.keySet();
    }

    /**
     * 获取所有Bean集合
     *
     * @return Bean集合
     */
    public Set<Object> getBeans() {
        return new HashSet<>(beanMap.values());
    }

    /**
     * 根据注解获取Class集合
     *
     * @param annotation
     * @return
     */
    public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation) {
        //1. 获取beanMap的所有class对象
        Set<Class<?>> classes = beanMap.keySet().stream().collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(classes)) {
            log.warn("Nothing in beanMap.");
            return null;
        }
        //2. 通过注解筛选被注解标记的class对象，并添加到classSet中
        Set<Class<?>> classSet = classes.stream()
                .filter(clazz -> clazz.isAnnotationPresent(annotation))
                .collect(Collectors.toSet());
        return CollectionUtils.isNotEmpty(classSet) ? classSet : null;
    }


    /**
     * 根据接口或者父类获取实现类或者子类的Class集合，不包括其本身
     *
     * @param interfaceOrClass 接口Class获取父类Class
     * @return Class集合
     */
    public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass) {
        //1. 获取beanMap的所有class对象
        Set<Class<?>> classes = beanMap.keySet().stream().collect(Collectors.toSet());
        if (CollectionUtils.isEmpty(classes)) {
            log.warn("Nothing in beanMap.");
            return null;
        }
        //2. 通过interfaceOrClass筛选class对象，并添加到classSet中
        Set<Class<?>> classSet = classes.stream()
                .filter(clazz -> interfaceOrClass.isAssignableFrom(clazz) && !Objects.equals(clazz, interfaceOrClass))
                .collect(Collectors.toSet());
        return classSet.size() > 0 ? classSet : null;
    }
}
